package com.xuecheng.content.service.impl;

import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.media.model.dto.MediaDTO;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.api.content.model.dto.TeachplanMediaDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.common.AuditEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseModeEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.content.agent.MediaApiAgent;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.convert.TeachplanConvert;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.TeachplanMedia;
import com.xuecheng.content.entity.ext.TeachplanNode;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 课程计划 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {
    @Autowired
    private CourseBaseService courseBaseService;
    @Autowired
    private MediaApiAgent mediaApiAgent;
    @Autowired
    private TeachplanMediaService teachplanMediaService;
    @Override
    public TeachplanDTO queryTreeNodes(Long courseId, Long companyId) {
        /*
         * 业务分析：
         *   1.是否开启事务
         *   2.判断关键数据
         *   3.判断业务数据
         *       课程基础信息
         *           判断是否存在
         *           判断是否是同一家教学机构
         *           判断是否删除
         *
         *   4.查询一门课程的课程计划list数据
         *   5.通过java的递归方式来生成树形结构
         *   6.将树形结构转为dto并返回
         * */
        //判断关键数据
        courseBaseService.getCourseBaseById(courseId, companyId);
// 4.查询一门课程的课程计划list数据
        TeachplanMapper baseMapper = this.getBaseMapper();
        List<TeachplanNode> teachplanNodes = baseMapper.selectByCourseId(courseId);
        //5.通过java的递归方式来生成树形结构
        //获取一级课程数据 并将一级数据删除
        TeachplanDTO resultDTO = null;
        if (ObjectUtils.isEmpty(teachplanNodes)) {
             resultDTO = new TeachplanDTO();
        } else {
            TeachplanNode node = teachplanNodes.remove(0);
            generateTreeNodes(node,teachplanNodes);
            //6将树形结构转为dto
             resultDTO = TeachplanConvert.INSTANCE.node2dto(node);
        }

        return resultDTO;
    }

    private void generateTreeNodes(TeachplanNode parent, List<TeachplanNode> nodes) {

        // 1.判断父级的集合属性是否为空，如果为空需要创建出来
        if (CollectionUtils.isEmpty(parent.getChildrenNodes())) {
            parent.setChildrenNodes(new ArrayList<>());
        }

        // 2.通过循环来实现递归，并生成树形结构
        for (TeachplanNode node : nodes) {

            // 将子集数据放到父级的集合中
            // 父级.id == 子级.parentId
            if (ObjectUtils.nullSafeEquals(parent.getId(),node.getParentid())) {
                parent.getChildrenNodes().add(node);

                // 如果不是三级课程计划，需要进行递归操作
                if (!(TeachPlanEnum.THIRD_LEVEL.equals(node.getGrade()))) {
                    // 递归方法来实现嵌套
                    generateTreeNodes(node,nodes);
                }

            }

        }

    }

    @Override
    @Transactional
    public TeachplanDTO createOrModifyTeachplan(TeachplanDTO dto, Long companyId) {
        TeachplanDTO resutDTO = null;
        if (ObjectUtils.isEmpty(dto.getTeachPlanId())) {
            //没有teachplanid-->新增
            resutDTO = createTeachplan(dto, companyId);
        } else {
            resutDTO = modifyTeachplan(dto,companyId);
        }

        return resutDTO;
    }

    @Override
    public TeachplanMediaDTO associateMedia(TeachplanMediaDTO dto, Long companyId) {
/*
    * 业务分析：
    *   1.判断关键数据
    *       teachplanId、mediaId,companyId
    *   2.判断业务数据
    *       课程的计划
    *           判断是否是第三级
    *           判断课程类型是否录播（课程基本信息中获得并判断）
    *           判断课程计划是否存在：teachplanId、courseId
    *
    *       课程基本信息
    *           判断是否存在或是否是同一家机构
    *           判断是否删除
    *           判断审核状态：未提交、审核未通过
    *
    *       媒资信息
    *           判断媒资信息是否存在
    *           判断是否是同一家机构
    *           判断媒资审核状态
    *   3.将dto数据转为po数据并保存到teachplanmedia表中
    *       添加或修改
    *           如果存在：
                    对课程计划的媒资信息进行修改：mediaId，mediaFileName
                如果不存在：
                    课程计划和媒资信息的添加
    *
    *   4.判断保存数据的结果并返回
    * */

//1.判断关键数据
        //     teachplanId、mediaId,companyId

        Long teachplanId = dto.getTeachplanId();
        Long mediaId = dto.getMediaId();
        if (ObjectUtils.isEmpty(teachplanId) ||
                ObjectUtils.isEmpty(mediaId) ||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        // 2.判断业务数据
        //    2.1   课程的计划
        //         判断课程计划是否存在：teachplanId、courseId
        //         判断是否是第三级
        //
        Teachplan teachplan = this.getById(teachplanId);
        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        Integer grade = teachplan.getGrade();
        if (!(TeachPlanEnum.THIRD_LEVEL.equals(grade))) {
            ExceptionCast.cast(ContentErrorCode.E_120410);
        }
        //   2.2 课程基本信息
        //         判断是否存在或是否是同一家机构
        //         判断是否删除
        //         判断审核状态：未提交、审核未通过
        //         判断课程类型是否录播（课程基本信息中获得并判断）
        Long courseId = teachplan.getCourseId();
        // 判断是否存在或是否是同一家机构   判断是否删除
        CourseBaseDTO courseBaseDTO = courseBaseService.getCourseBaseById(courseId, companyId);
        // 判断审核状态：未提交、审核未通过
        String auditStatus = courseBaseDTO.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }
        // 判断课程类型是否录播（课程基本信息中获得并判断）
        String teachmode = courseBaseDTO.getTeachmode();
        if (!(CourseModeEnum.COURSE_MODE_RECORD_STATUS.getCode().equals(teachmode))) {
            ExceptionCast.cast(ContentErrorCode.E_120416);
        }
        //     媒资信息
        //         判断媒资信息是否存在
        //         判断是否是同一家机构
        //         判断媒资审核状态
        RestResponse<MediaDTO> response = mediaApiAgent.getById(mediaId);
        if (!(response.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(response.getCode(),response.getMsg());
        }
        // 判断媒资信息是否存在
        MediaDTO mediaDTO = response.getResult();
        if (ObjectUtils.isEmpty(mediaDTO)) {
            ExceptionCast.cast(ContentErrorCode.E_120417);
        }
        // 判断是否是同一家机构
        if (!(ObjectUtils.nullSafeEquals(mediaDTO.getCompanyId(),companyId))) {
            ExceptionCast.cast(CommonErrorCode.E_403000);
        }
        String mediaAuditStatus = mediaDTO.getAuditStatus();
        // 判断媒资审核状态
        if (!(AuditEnum.AUDIT_PASTED_STATUS.getCode().equals(mediaAuditStatus))) {
            ExceptionCast.cast(ContentErrorCode.E_120418);
        }
        // 3.将dto数据转为po数据并保存到teachplanmedia表中
        //     添加或修改
        //         如果存在：
        //             对课程计划的媒资信息进行修改：mediaId，mediaFileName
        //         如果不存在：
        //             课程计划和媒资信息的添加
        LambdaQueryWrapper<TeachplanMedia> teachplanMediaQueryWrapper = new LambdaQueryWrapper<>();
        teachplanMediaQueryWrapper.eq(TeachplanMedia::getTeachplanId,teachplanId);
        teachplanMediaQueryWrapper.eq(TeachplanMedia::getCourseId,courseId);
        TeachplanMedia teachplanMedia = teachplanMediaService.getOne(teachplanMediaQueryWrapper);
        boolean result = false;
        if (ObjectUtils.isEmpty(teachplanMedia)) {
            // 添加操作
            teachplanMedia = new TeachplanMedia();
            teachplanMedia.setMediaId(mediaId);
            teachplanMedia.setTeachplanId(teachplanId);
            teachplanMedia.setCourseId(courseId);
            teachplanMedia.setMediaFilename(mediaDTO.getFilename());
            result = teachplanMediaService.save(teachplanMedia);
        } else {
            // 修改操作: 改为UpdateWrapper
            teachplanMedia.setMediaFilename(mediaDTO.getFilename());
            teachplanMedia.setMediaId(mediaId);
            result = teachplanMediaService.updateById(teachplanMedia);
        }
        // 4.判断保存数据的结果并返回
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120411);
        }
        TeachplanMediaDTO resultDTO = TeachplanConvert.INSTANCE.po2dto(teachplanMedia);
        return resultDTO;
    }


    private TeachplanDTO modifyTeachplan(TeachplanDTO dto, Long companyId) {
        return null;
    }
    /*
     * 新增课程计划业务分析：
     *   1.判断关键数据
     *       课程计划的名称不能为空
     *       课程id不能为空
     *       教学机构id不能为空
     *
     *   2.判断业务数据
     *       课程基础信息
     *           判断是否存在
     *           判断是否是同一家教学机构
     *           判断是否删除
     *           判断审核状态：未提交、审核未通过
     *
     *   3.获得新增课程计划的父级数据
     *       给新增课程计划赋值
     *           grade：父级的grade+1
     *           parentid：父级的id
     *           orderby：select max(order) from teachplan where parentid = 父级id
     *
     *
     *   4.将dto转为po并保存到数据库中
     *       判断保存后的结果
     *   5.将数据库最新的数据转为dto并返回
     * */
    private TeachplanDTO createTeachplan(TeachplanDTO dto, Long companyId) {

        //1.判断关键数据
        //     课程计划的名称不能为空
        //     课程id不能为空
        //     教学机构id不能为空
        if (ObjectUtils.isEmpty(dto.getPname())||
                ObjectUtils.isEmpty(dto.getCourseId())||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        // 2.判断业务数据
        //     课程基础信息
        //         判断是否存在
        //         判断是否是同一家教学机构
        //         判断是否删除
        //         判断审核状态：未提交、审核未通过
        CourseBaseDTO courseBase = courseBaseService.getCourseBaseById(dto.getCourseId(), companyId);


        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }

        // 3.获得新增课程计划的父级数据
        //     给新增课程计划赋值
        //         grade：父级的grade+1
        //         parentid：父级的id
        //         orderby：select max(order) from teachplan where parentid = 父级id
        Teachplan parentNode = generateParentNode(dto,courseBase);

        int childrenGrade = parentNode.getGrade() + 1;
        dto.setGrade(childrenGrade+"");
        dto.setParentid(parentNode.getId());

        TeachplanMapper baseMapper = this.getBaseMapper();
        Integer orderBy = baseMapper.selectOrderByParentId(parentNode.getId());

        // 如果查询出orderBy为null --> 默认赋值为1
        // 如果查询出orderBy为不null --> orderBy+1
        if (ObjectUtils.isEmpty(orderBy)) {
            dto.setOrderby(TeachPlanEnum.FIRST_LEVEL);
        } else {
            dto.setOrderby(orderBy+1);
        }


        // 4.将dto转为po并保存到数据库中
        Teachplan teachplan = TeachplanConvert.INSTANCE.dto2entity(dto);

        boolean result = this.save(teachplan);
        //     判断保存后的结果
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }


        // 5.将数据库最新的数据转为dto并返回
        Teachplan po = this.getById(teachplan.getId());

        TeachplanDTO resultDTO = TeachplanConvert.INSTANCE.entity2dto(po);

        return resultDTO;
    }
    /*
     * 业务分析：
     *   1.判断新增数据是二级还是三级课程计划
     *       判断新增数据是否有parentId值
     *           如果有：三级课程计划
     *           如果没有：二级课程计划
     *   2.获得二级课程计划的父级数据
     *       查询数据库获得一级课程计划：courseId、parentid=0
     *           如果没有：自动创建并保存数据--数据来源 coursebase
     *           如果有：直接返回
     *   3.获得三级课程计划的父级数据
     *       查询数据库获得二级课程计划：parentId
     *           如果没有：直接抛出异常
     *           如果有：直接返回
     * */
    private Teachplan generateParentNode(TeachplanDTO dto, CourseBaseDTO courseBase) {

        //1.判断新增数据是二级还是三级课程计划
        //     判断新增数据是否有parentId值
        //         如果有：三级课程计划
        //         如果没有：二级课程计划
        Long parentid = dto.getParentid();
        if (ObjectUtils.isEmpty(parentid)) {
            // 2.获得二级课程计划的父级数据
            //     查询数据库获得一级课程计划：courseId、parentid=0
            //         如果没有：自动创建并保存数据--数据来源 coursebase
            //         如果有：直接返回
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getCourseId, courseBase.getCourseBaseId());
            queryWrapper.eq(Teachplan::getParentid, TeachPlanEnum.FIRST_PARENTID_FLAG);

            Teachplan rootParent = this.getOne(queryWrapper);

            if (ObjectUtils.isEmpty(rootParent)) {

                rootParent = new Teachplan();
                rootParent.setPname(courseBase.getName());
                rootParent.setParentid(new Long(TeachPlanEnum.FIRST_PARENTID_FLAG));
                rootParent.setGrade(TeachPlanEnum.FIRST_LEVEL);
                rootParent.setDescription(courseBase.getDescription());
                rootParent.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                rootParent.setCourseId(courseBase.getCourseBaseId());
                boolean save = this.save(rootParent);

                if (!save) {
                    ExceptionCast.cast(ContentErrorCode.E_120415);
                }
            }

            return rootParent;

        } else {
            // 3.获得三级课程计划的父级数据
            //     查询数据库获得二级课程计划：parentId
            //         如果没有：直接抛出异常
            //         如果有：直接返回
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getId, parentid);

            Teachplan teachplan = this.getOne(queryWrapper);

            if (ObjectUtils.isEmpty(teachplan)) {
                ExceptionCast.cast(ContentErrorCode.E_120408);
            }

            return teachplan;
        }


    }
}
